<?php

/**
 * JPGraph v4.0.3
 */

namespace lemon\JpGraph\Text;

use lemon\JpGraph\Util;

/**
 * @class GTextTable
 * // Description:
 * // Graphic text table
 */
class GTextTable
{
	public $iCells   = [];
	public $iSize	= [0, 0]; // Need to be public since they are used by the cell
	private $iWidth  = 0;
	private $iHeight = 0;
	private $iColWidth;
	private $iRowHeight;
	private $iImg;
	private $iXPos = 0;
	private $iYPos = 0;
	private $iScaleXPos;
	private $iScaleYPos;
	private $iBGColor	  = '';
	private $iBorderColor  = 'black';
	private $iBorderWeight = 1;
	private $iInit		 = false;
	private $iYAnchor	  = 'top';
	private $iXAnchor	  = 'left';

	/**
	 * First and second phase constructors
	 *-----------------------------------------------------------------.
	 */
	public function __construct(){
		// Empty
	}

	public function Init($aRows = 0, $aCols = 0, $aFillText = ''){
		$this->iSize[0] = $aRows;
		$this->iSize[1] = $aCols;
		for ($i = 0; $i < $this->iSize[0]; ++$i) {
			for ($j = 0; $j < $this->iSize[1]; ++$j) {
				$this->iCells[$i][$j] = new GTextTableCell($aFillText, $i, $j);
				$this->iCells[$i][$j]->Init($this);
			}
		}
		$this->iInit = true;
	}

	/**
	 * Outer border of table
	 *-----------------------------------------------------------------.
	 *
	 * @param mixed $aWeight
	 * @param mixed $aColor
	 */
	public function SetBorder($aWeight = 1, $aColor = 'black'){
		$this->iBorderColor  = $aColor;
		$this->iBorderWeight = $aWeight;
	}

	/**
	 * Position in graph of table
	 *-----------------------------------------------------------------.
	 *
	 * @param mixed $aX
	 * @param mixed $aY
	 */
	public function SetPos($aX, $aY){
		$this->iXPos = $aX;
		$this->iYPos = $aY;
	}

	public function SetScalePos($aX, $aY){
		$this->iScaleXPos = $aX;
		$this->iScaleYPos = $aY;
	}

	public function SetAnchorPos($aXAnchor, $aYAnchor = 'top'){
		$this->iXAnchor = $aXAnchor;
		$this->iYAnchor = $aYAnchor;
	}

	/**
	 * Setup country flag in a cell
	 *-----------------------------------------------------------------.
	 *
	 * @param mixed $aRow
	 * @param mixed $aCol
	 * @param mixed $aFlag
	 * @param mixed $aScale
	 * @param mixed $aMix
	 * @param mixed $aStdSize
	 */
	public function SetCellCountryFlag($aRow, $aCol, $aFlag, $aScale = 1.0, $aMix = 100, $aStdSize = 3){
		$this->_chkR($aRow);
		$this->_chkC($aCol);
		$this->iCells[$aRow][$aCol]->SetCountryFlag($aFlag, $aScale, $aMix, $aStdSize);
	}

	/**
	 * Setup image in a cell
	 *-----------------------------------------------------------------.
	 *
	 * @param mixed $aRow
	 * @param mixed $aCol
	 * @param mixed $aFile
	 * @param mixed $aScale
	 * @param mixed $aMix
	 */
	public function SetCellImage($aRow, $aCol, $aFile, $aScale = 1.0, $aMix = 100){
		$this->_chkR($aRow);
		$this->_chkC($aCol);
		$this->iCells[$aRow][$aCol]->SetImage($aFile, $aScale, $aMix);
	}

	public function SetRowImage($aRow, $aFile, $aScale = 1.0, $aMix = 100){
		$this->_chkR($aRow);
		for ($j = 0; $j < $this->iSize[1]; ++$j) {
			$this->iCells[$aRow][$j]->SetImage($aFile, $aScale, $aMix);
		}
	}

	public function SetColImage($aCol, $aFile, $aScale = 1.0, $aMix = 100){
		$this->_chkC($aCol);
		for ($j = 0; $j < $this->iSize[0]; ++$j) {
			$this->iCells[$j][$aCol]->SetImage($aFile, $aScale, $aMix);
		}
	}

	public function SetImage($aFileR1, $aScaleC1 = null, $aMixR2 = null, $aC2 = null, $aFile = null, $aScale = 1.0, $aMix = 100){
		if ($aScaleC1 !== null && $aMixR2 !== null && $aC2 !== null && $aFile !== null) {
			$this->_chkR($aArgR1);
			$this->_chkC($aC1);
			$this->_chkR($aR2);
			$this->_chkC($aC2);
		} else {
			if ($aScaleC1 !== null) {
				$aScale = $aScaleC1;
			}

			if ($aMixR2 !== null) {
				$aMix = $aMixR2;
			}

			$aFile	= $aFileR1;
			$aMixR2   = $this->iSize[0] - 1;
			$aFileR1  = 0;
			$aC2	  = $this->iSize[1] - 1;
			$aScaleC1 = 0;
		}
		for ($i = $aArgR1; $i <= $aR2; ++$i) {
			for ($j = $aC1; $j <= $aC2; ++$j) {
				$this->iCells[$i][$j]->SetImage($aFile, $aScale, $aMix);
			}
		}
	}

	public function SetCellImageConstrain($aRow, $aCol, $aType, $aVal){
		$this->_chkR($aRow);
		$this->_chkC($aCol);
		$this->iCells[$aRow][$aCol]->SetImageConstrain($aType, $aVal);
	}

	/**
	 * Generate a HTML version of the table
	 *-----------------------------------------------------------------.
	 */
	public function toString(){
		$t = '<table border=1 cellspacing=0 cellpadding=0>';
		for ($i = 0; $i < $this->iSize[0]; ++$i) {
			$t .= '<tr>';
			for ($j = 0; $j < $this->iSize[1]; ++$j) {
				$t .= '<td>';
				if ($this->iCells[$i][$j]->iMerged) {
					$t .= 'M ';
				}

				$t .= 'val=' . $this->iCells[$i][$j]->iVal->t;
				$t .= ' (cs=' . $this->iCells[$i][$j]->iColSpan .
				', rs=' . $this->iCells[$i][$j]->iRowSpan . ')';
				$t .= '</td>';
			}
			$t .= '</tr>';
		}
		$t .= '</table>';

		return $t;
	}

	/**
	 * Specify data for table
	 *-----------------------------------------------------------------.
	 *
	 * @param mixed	  $aArg1
	 * @param null|mixed $aArg2
	 * @param null|mixed $aArg3
	 */
	public function Set($aArg1, $aArg2 = null, $aArg3 = null){
		if ($aArg2 === null && $aArg3 === null) {
			if (is_array($aArg1)) {
				if (is_array($aArg1[0])) {
					$m = safe_count($aArg1);
					// Find the longest row
					$n = 0;
					for ($i = 0; $i < $m; ++$i) {
						$n = max(safe_count($aArg1[$i]), $n);
					}

					for ($i = 0; $i < $m; ++$i) {
						for ($j = 0; $j < $n; ++$j) {
							if (isset($aArg1[$i][$j])) {
								$this->_setcell($i, $j, (string) $aArg1[$i][$j]);
							} else {
								$this->_setcell($i, $j);
							}
						}
					}
					$this->iSize[0] = $m;
					$this->iSize[1] = $n;
					$this->iInit	= true;
				} else {
					Util\JpGraphError::RaiseL(27001);
					//('Illegal argument to GTextTable::Set(). Array must be 2 dimensional');
				}
			} else {
				Util\JpGraphError::RaiseL(27002);
				//('Illegal argument to GTextTable::Set()');
			}
		} else {
			// Must be in the form (row,col,val)
			$this->_chkR($aArg1);
			$this->_chkC($aArg2);
			$this->_setcell($aArg1, $aArg2, (string) $aArg3);
		}
	}

	/**
	 * Cell margin setting
	 *---------------------------------------------------------------------.
	 *
	 * @param mixed	  $aArgR1
	 * @param null|mixed $aC1
	 * @param null|mixed $aR2
	 * @param null|mixed $aC2
	 * @param null|mixed $aPad
	 */
	public function SetPadding($aArgR1, $aC1 = null, $aR2 = null, $aC2 = null, $aPad = null){
		if ($aC1 !== null && $aR2 !== null && $aC2 !== null && $aPad !== null) {
			$this->_chkR($aArgR1);
			$this->_chkC($aC1);
			$this->_chkR($aR2);
			$this->_chkC($aC2);
		} else {
			$aPad   = $aArgR1;
			$aR2	= $this->iSize[0] - 1;
			$aArgR1 = 0;
			$aC2	= $this->iSize[1] - 1;
			$aC1	= 0;
		}
		for ($i = $aArgR1; $i <= $aR2; ++$i) {
			for ($j = $aC1; $j <= $aC2; ++$j) {
				$this->iCells[$i][$j]->SetMargin($aPad, $aPad, $aPad, $aPad);
			}
		}
	}

	public function SetRowPadding($aRow, $aPad){
		$this->_chkR($aRow);
		for ($j = 0; $j < $this->iSize[1]; ++$j) {
			$this->iCells[$aRow][$j]->SetMargin($aPad, $aPad, $aPad, $aPad);
		}
	}

	public function SetColPadding($aCol, $aPad){
		$this->_chkC($aCol);
		for ($j = 0; $j < $this->iSize[0]; ++$j) {
			$this->iCells[$j][$aCol]->SetMargin($aPad, $aPad, $aPad, $aPad);
		}
	}

	public function SetCellPadding($aRow, $aCol, $aPad){
		$this->_chkR($aRow);
		$this->_chkC($aCol);
		$this->iCells[$aRow][$aCol]->SetMargin($aPad, $aPad, $aPad, $aPad);
	}

	/**
	 * Cell text orientation setting
	 *---------------------------------------------------------------------.
	 *
	 * @param mixed	  $aArgR1
	 * @param null|mixed $aC1
	 * @param null|mixed $aR2
	 * @param null|mixed $aC2
	 * @param null|mixed $aO
	 */
	public function SetTextOrientation($aArgR1, $aC1 = null, $aR2 = null, $aC2 = null, $aO = null){
		if ($aC1 !== null && $aR2 !== null && $aC2 !== null && $aPad !== null) {
			$this->_chkR($aArgR1);
			$this->_chkC($aC1);
			$this->_chkR($aR2);
			$this->_chkC($aC2);
		} else {
			$aO	 = $aArgR1;
			$aR2	= $this->iSize[0] - 1;
			$aArgR1 = 0;
			$aC2	= $this->iSize[1] - 1;
			$aC1	= 0;
		}
		for ($i = $aArgR1; $i <= $aR2; ++$i) {
			for ($j = $aC1; $j <= $aC2; ++$j) {
				$this->iCells[$i][$j]->iVal->SetOrientation($aO);
			}
		}
	}

	public function SetRowTextOrientation($aRow, $aO){
		$this->_chkR($aRow);
		for ($j = 0; $j < $this->iSize[1]; ++$j) {
			$this->iCells[$aRow][$j]->iVal->SetOrientation($aO);
		}
	}

	public function SetColTextOrientation($aCol, $aO){
		$this->_chkC($aCol);
		for ($j = 0; $j < $this->iSize[0]; ++$j) {
			$this->iCells[$j][$aCol]->iVal->SetOrientation($aO);
		}
	}

	public function SetCellTextOrientation($aRow, $aCol, $aO){
		$this->_chkR($aRow);
		$this->_chkC($aCol);
		$this->iCells[$aRow][$aCol]->iVal->SetOrientation($aO);
	}

	/**
	 * Font color setting
	 *---------------------------------------------------------------------.
	 *
	 * @param mixed	  $aArgR1
	 * @param null|mixed $aC1
	 * @param null|mixed $aR2
	 * @param null|mixed $aC2
	 * @param null|mixed $aArg
	 */
	public function SetColor($aArgR1, $aC1 = null, $aR2 = null, $aC2 = null, $aArg = null){
		if ($aC1 !== null && $aR2 !== null && $aC2 !== null && $aArg !== null) {
			$this->_chkR($aArgR1);
			$this->_chkC($aC1);
			$this->_chkR($aR2);
			$this->_chkC($aC2);
		} else {
			$aArg   = $aArgR1;
			$aR2	= $this->iSize[0] - 1;
			$aArgR1 = 0;
			$aC2	= $this->iSize[1] - 1;
			$aC1	= 0;
		}
		for ($i = $aArgR1; $i <= $aR2; ++$i) {
			for ($j = $aC1; $j <= $aC2; ++$j) {
				$this->iCells[$i][$j]->SetFontColor($aArg);
			}
		}
	}

	public function SetRowColor($aRow, $aColor){
		$this->_chkR($aRow);
		for ($j = 0; $j < $this->iSize[1]; ++$j) {
			$this->iCells[$aRow][$j]->SetFontColor($aColor);
		}
	}

	public function SetColColor($aCol, $aColor){
		$this->_chkC($aCol);
		for ($i = 0; $i < $this->iSize[0]; ++$i) {
			$this->iCells[$i][$aCol]->SetFontColor($aColor);
		}
	}

	public function SetCellColor($aRow, $aCol, $aColor){
		$this->_chkR($aRow);
		$this->_chkC($aCol);
		$this->iCells[$aRow][$aCol]->SetFontColor($aColor);
	}

	/**
	 * Fill color settings
	 *---------------------------------------------------------------------.
	 *
	 * @param mixed	  $aArgR1
	 * @param null|mixed $aC1
	 * @param null|mixed $aR2
	 * @param null|mixed $aC2
	 * @param null|mixed $aArg
	 */
	public function SetFillColor($aArgR1, $aC1 = null, $aR2 = null, $aC2 = null, $aArg = null){
		if ($aC1 !== null && $aR2 !== null && $aC2 !== null && $aArg !== null) {
			$this->_chkR($aArgR1);
			$this->_chkC($aC1);
			$this->_chkR($aR2);
			$this->_chkC($aC2);
			for ($i = $aArgR1; $i <= $aR2; ++$i) {
				for ($j = $aC1; $j <= $aC2; ++$j) {
					$this->iCells[$i][$j]->SetFillColor($aArg);
				}
			}
		} else {
			$this->iBGColor = $aArgR1;
		}
	}

	public function SetRowFillColor($aRow, $aColor){
		$this->_chkR($aRow);
		for ($j = 0; $j < $this->iSize[1]; ++$j) {
			$this->iCells[$aRow][$j]->SetFillColor($aColor);
		}
	}

	public function SetColFillColor($aCol, $aColor){
		$this->_chkC($aCol);
		for ($i = 0; $i < $this->iSize[0]; ++$i) {
			$this->iCells[$i][$aCol]->SetFillColor($aColor);
		}
	}

	public function SetCellFillColor($aRow, $aCol, $aColor){
		$this->_chkR($aRow);
		$this->_chkC($aCol);
		$this->iCells[$aRow][$aCol]->SetFillColor($aColor);
	}

	/**
	 * Font family setting
	 *---------------------------------------------------------------------.
	 */
	public function SetFont(){
		$numargs = func_num_args();
		if ($numargs == 2 || $numargs == 3) {
			$aFF = func_get_arg(0);
			$aFS = func_get_arg(1);
			if ($numargs == 3) {
				$aFSize = func_get_arg(2);
			} else {
				$aFSize = 10;
			}

			$aR2 = $this->iSize[0] - 1;
			$aR1 = 0;
			$aC2 = $this->iSize[1] - 1;
			$aC1 = 0;
		} elseif ($numargs == 6 || $numargs == 7) {
			$aR1 = func_get_arg(0);
			$aC1 = func_get_arg(1);
			$aR2 = func_get_arg(2);
			$aC2 = func_get_arg(3);
			$aFF = func_get_arg(4);
			$aFS = func_get_arg(5);
			if ($numargs == 7) {
				$aFSize = func_get_arg(6);
			} else {
				$aFSize = 10;
			}
		} else {
			Util\JpGraphError::RaiseL(27003);
			//('Wrong number of arguments to GTextTable::SetColor()');
		}
		$this->_chkR($aR1);
		$this->_chkC($aC1);
		$this->_chkR($aR2);
		$this->_chkC($aC2);
		for ($i = $aR1; $i <= $aR2; ++$i) {
			for ($j = $aC1; $j <= $aC2; ++$j) {
				$this->iCells[$i][$j]->SetFont($aFF, $aFS, $aFSize);
			}
		}
	}

	public function SetRowFont($aRow, $aFF, $aFS, $aFSize = 10){
		$this->_chkR($aRow);
		for ($j = 0; $j < $this->iSize[1]; ++$j) {
			$this->iCells[$aRow][$j]->SetFont($aFF, $aFS, $aFSize);
		}
	}

	public function SetColFont($aCol, $aFF, $aFS, $aFSize = 10){
		$this->_chkC($aCol);
		for ($i = 0; $i < $this->iSize[0]; ++$i) {
			$this->iCells[$i][$aCol]->SetFont($aFF, $aFS, $aFSize);
		}
	}

	public function SetCellFont($aRow, $aCol, $aFF, $aFS, $aFSize = 10){
		$this->_chkR($aRow);
		$this->_chkC($aCol);
		$this->iCells[$aRow][$aCol]->SetFont($aFF, $aFS, $aFSize);
	}

	/**
	 * Cell align settings
	 *---------------------------------------------------------------------.
	 *
	 * @param null|mixed $aR1HAlign
	 * @param null|mixed $aC1VAlign
	 * @param null|mixed $aR2
	 * @param null|mixed $aC2
	 * @param null|mixed $aHArg
	 * @param mixed	  $aVArg
	 */
	public function SetAlign($aR1HAlign = null, $aC1VAlign = null, $aR2 = null, $aC2 = null, $aHArg = null, $aVArg = 'center'){
		if ($aC1VAlign !== null && $aR2 !== null && $aC2 !== null && $aHArg !== null) {
			$this->_chkR($aR1HAlign);
			$this->_chkC($aC1VAlign);
			$this->_chkR($aR2);
			$this->_chkC($aC2);
		} else {
			if ($aR1HAlign === null) {
				Util\JpGraphError::RaiseL(27010);
			}
			if ($aC1VAlign === null) {
				$aC1VAlign = 'center';
			}
			$aHArg	 = $aR1HAlign;
			$aVArg	 = $aC1VAlign === null ? 'center' : $aC1VAlign;
			$aR2	   = $this->iSize[0] - 1;
			$aR1HAlign = 0;
			$aC2	   = $this->iSize[1] - 1;
			$aC1VAlign = 0;
		}
		for ($i = $aR1HAlign; $i <= $aR2; ++$i) {
			for ($j = $aC1VAlign; $j <= $aC2; ++$j) {
				$this->iCells[$i][$j]->SetAlign($aHArg, $aVArg);
			}
		}
	}

	public function SetCellAlign($aRow, $aCol, $aHorAlign, $aVertAlign = 'bottom'){
		$this->_chkR($aRow);
		$this->_chkC($aCol);
		$this->iCells[$aRow][$aCol]->SetAlign($aHorAlign, $aVertAlign);
	}

	public function SetRowAlign($aRow, $aHorAlign, $aVertAlign = 'bottom'){
		$this->_chkR($aRow);
		for ($j = 0; $j < $this->iSize[1]; ++$j) {
			$this->iCells[$aRow][$j]->SetAlign($aHorAlign, $aVertAlign);
		}
	}

	public function SetColAlign($aCol, $aHorAlign, $aVertAlign = 'bottom'){
		$this->_chkC($aCol);
		for ($i = 0; $i < $this->iSize[0]; ++$i) {
			$this->iCells[$i][$aCol]->SetAlign($aHorAlign, $aVertAlign);
		}
	}

	/**
	 * Cell number format
	 *---------------------------------------------------------------------.
	 *
	 * @param mixed	  $aArgR1
	 * @param null|mixed $aC1
	 * @param null|mixed $aR2
	 * @param null|mixed $aC2
	 * @param null|mixed $aArg
	 */
	public function SetNumberFormat($aArgR1, $aC1 = null, $aR2 = null, $aC2 = null, $aArg = null){
		if ($aC1 !== null && $aR2 !== null && $aC2 !== null && $aArg !== null) {
			$this->_chkR($aArgR1);
			$this->_chkC($aC1);
			$this->_chkR($aR2);
			$this->_chkC($aC2);
		} else {
			$aArg   = $aArgR1;
			$aR2	= $this->iSize[0] - 1;
			$aArgR1 = 0;
			$aC2	= $this->iSize[1] - 1;
			$aC1	= 0;
		}
		if (!is_string($aArg)) {
			Util\JpGraphError::RaiseL(27013); // argument must be a string
		}
		for ($i = $aArgR1; $i <= $aR2; ++$i) {
			for ($j = $aC1; $j <= $aC2; ++$j) {
				$this->iCells[$i][$j]->SetNumberFormat($aArg);
			}
		}
	}

	public function SetRowNumberFormat($aRow, $aF){
		$this->_chkR($aRow);
		if (!is_string($aF)) {
			Util\JpGraphError::RaiseL(27013); // argument must be a string
		}
		for ($j = 0; $j < $this->iSize[1]; ++$j) {
			$this->iCells[$aRow][$j]->SetNumberFormat($aF);
		}
	}

	public function SetColNumberFormat($aCol, $aF){
		$this->_chkC($aCol);
		if (!is_string($aF)) {
			Util\JpGraphError::RaiseL(27013); // argument must be a string
		}
		for ($i = 0; $i < $this->iSize[0]; ++$i) {
			$this->iCells[$i][$aCol]->SetNumberFormat($aF);
		}
	}

	public function SetCellNumberFormat($aRow, $aCol, $aF){
		$this->_chkR($aRow);
		$this->_chkC($aCol);
		if (!is_string($aF)) {
			Util\JpGraphError::RaiseL(27013); // argument must be a string
		}
		$this->iCells[$aRow][$aCol]->SetNumberFormat($aF);
	}

	/**
	 * Set row and column min size
	 *---------------------------------------------------------------------.
	 *
	 * @param mixed	  $aColWidth
	 * @param null|mixed $aWidth
	 */
	public function SetMinColWidth($aColWidth, $aWidth = null){
		// If there is only one argument this means that all
		// columns get set to the same width
		if ($aWidth === null) {
			for ($i = 0; $i < $this->iSize[1]; ++$i) {
				$this->iColWidth[$i] = $aColWidth;
			}
		} else {
			$this->_chkC($aColWidth);
			$this->iColWidth[$aColWidth] = $aWidth;
		}
	}

	public function SetMinRowHeight($aRowHeight, $aHeight = null){
		// If there is only one argument this means that all
		// rows get set to the same height
		if ($aHeight === null) {
			for ($i = 0; $i < $this->iSize[0]; ++$i) {
				$this->iRowHeight[$i] = $aRowHeight;
			}
		} else {
			$this->_chkR($aRowHeight);
			$this->iRowHeight[$aRowHeight] = $aHeight;
		}
	}

	/**
	 * Grid line settings
	 *---------------------------------------------------------------------.
	 *
	 * @param mixed $aWeight
	 * @param mixed $aColor
	 * @param mixed $aStyle
	 */
	public function SetGrid($aWeight = 1, $aColor = 'black', $aStyle = TGRID_SINGLE){
		$rc = $this->iSize[0];
		$cc = $this->iSize[1];
		for ($i = 0; $i < $rc; ++$i) {
			for ($j = 0; $j < $cc; ++$j) {
				$this->iCells[$i][$j]->SetGridColor($aColor, $aColor);
				$this->iCells[$i][$j]->SetGridWeight($aWeight, $aWeight);
				$this->iCells[$i][$j]->SetGridStyle($aStyle);
			}
		}
	}

	public function SetColGrid($aCol, $aWeight = 1, $aColor = 'black', $aStyle = TGRID_SINGLE){
		$this->_chkC($aCol);
		for ($i = 0; $i < $this->iSize[0]; ++$i) {
			$this->iCells[$i][$aCol]->SetGridWeight($aWeight);
			$this->iCells[$i][$aCol]->SetGridColor($aColor);
			$this->iCells[$i][$aCol]->SetGridStyle($aStyle);
		}
	}

	public function SetRowGrid($aRow, $aWeight = 1, $aColor = 'black', $aStyle = TGRID_SINGLE){
		$this->_chkR($aRow);
		for ($j = 0; $j < $this->iSize[1]; ++$j) {
			$this->iCells[$aRow][$j]->SetGridWeight(null, $aWeight);
			$this->iCells[$aRow][$j]->SetGridColor(null, $aColor);
			$this->iCells[$aRow][$j]->SetGridStyle(null, $aStyle);
		}
	}

	/**
	 * Merge cells
	 *---------------------------------------------------------------------.
	 *
	 * @param mixed $aRow
	 * @param mixed $aHAlign
	 * @param mixed $aVAlign
	 */
	public function MergeRow($aRow, $aHAlign = 'center', $aVAlign = 'center'){
		$this->_chkR($aRow);
		$this->MergeCells($aRow, 0, $aRow, $this->iSize[1] - 1, $aHAlign, $aVAlign);
	}

	public function MergeCol($aCol, $aHAlign = 'center', $aVAlign = 'center'){
		$this->_chkC($aCol);
		$this->MergeCells(0, $aCol, $this->iSize[0] - 1, $aCol, $aHAlign, $aVAlign);
	}

	public function MergeCells($aR1, $aC1, $aR2, $aC2, $aHAlign = 'center', $aVAlign = 'center'){
		if ($aR1 > $aR2 || $aC1 > $aC2) {
			Util\JpGraphError::RaiseL(27004);
			//('GTextTable::MergeCells(). Specified cell range to be merged is not valid.');
		}
		$this->_chkR($aR1);
		$this->_chkC($aC1);
		$this->_chkR($aR2);
		$this->_chkC($aC2);
		$rspan = $aR2 - $aR1 + 1;
		$cspan = $aC2 - $aC1 + 1;
		// Setup the parent cell for this merged group
		if ($this->iCells[$aR1][$aC1]->IsMerged()) {
			Util\JpGraphError::RaiseL(27005, $aR1, $aC1, $aR2, $aC2);
			//("Cannot merge already merged cells in the range ($aR1,$aC1), ($aR2,$aC2)");
		}
		$this->iCells[$aR1][$aC1]->SetRowColSpan($rspan, $cspan);
		$this->iCells[$aR1][$aC1]->SetAlign($aHAlign, $aVAlign);
		for ($i = $aR1; $i <= $aR2; ++$i) {
			for ($j = $aC1; $j <= $aC2; ++$j) {
				if (!($i == $aR1 && $j == $aC1)) {
					if ($this->iCells[$i][$j]->IsMerged()) {
						Util\JpGraphError::RaiseL(27005, $aR1, $aC1, $aR2, $aC2);
						//("Cannot merge already merged cells in the range ($aR1,$aC1), ($aR2,$aC2)");
					}
					$this->iCells[$i][$j]->SetMerged($aR1, $aC1, true);
				}
			}
		}
	}

	/**
	 * CSIM methods
	 *---------------------------------------------------------------------.
	 *
	 * @param mixed	  $aTarget
	 * @param null|mixed $aAlt
	 * @param mixed	  $aAutoTarget
	 */
	public function SetCSIMTarget($aTarget, $aAlt = null, $aAutoTarget = false){
		$m	= $this->iSize[0];
		$n	= $this->iSize[1];
		$csim = '';
		for ($i = 0; $i < $m; ++$i) {
			for ($j = 0; $j < $n; ++$j) {
				if ($aAutoTarget) {
					$t = $aTarget . "?row=${i}&col=${j}";
				} else {
					$t = $aTarget;
				}

				$this->iCells[$i][$j]->SetCSIMTarget($t, $aAlt);
			}
		}
	}

	public function SetCellCSIMTarget($aRow, $aCol, $aTarget, $aAlt = null){
		$this->_chkR($aRow);
		$this->_chkC($aCol);
		$this->iCells[$aRow][$aCol]->SetCSIMTarget($aTarget, $aAlt);
	}

	/**
	 * Private methods
	 *---------------------------------------------------------------------.
	 */
	public function GetCSIMAreas(){
		$m	= $this->iSize[0];
		$n	= $this->iSize[1];
		$csim = '';
		for ($i = 0; $i < $m; ++$i) {
			for ($j = 0; $j < $n; ++$j) {
				$csim .= $this->iCells[$i][$j]->GetCSIMArea();
			}
		}

		return $csim;
	}

	public function _chkC($aCol){
		if (!$this->iInit) {
			Util\JpGraphError::Raise(27014); // Table not initialized
		}
		if ($aCol < 0 || $aCol >= $this->iSize[1]) {
			Util\JpGraphError::RaiseL(27006, $aCol);
		}

		//("GTextTable:\nColumn argument ($aCol) is outside specified table size.");
	}

	public function _chkR($aRow){
		if (!$this->iInit) {
			Util\JpGraphError::Raise(27014); // Table not initialized
		}
		if ($aRow < 0 || $aRow >= $this->iSize[0]) {
			Util\JpGraphError::RaiseL(27007, $aRow);
		}

		//("GTextTable:\nRow argument ($aRow) is outside specified table size.");
	}

	public function _getScalePos(){
		if ($this->iScaleXPos === null || $this->iScaleYPos === null) {
			return false;
		}

		return [$this->iScaleXPos, $this->iScaleYPos];
	}

	public function _autoSizeTable($aImg){
		// Get maximum column width and row height
		$m = $this->iSize[0];
		$n = $this->iSize[1];
		$w = 1;
		$h = 1;

		// Get maximum row height per row
		for ($i = 0; $i < $m; ++$i) {
			$h = 0;
			for ($j = 0; $j < $n; ++$j) {
				$h = max($h, $this->iCells[$i][$j]->GetHeight($aImg));
			}
			if (isset($this->iRowHeight[$i])) {
				$this->iRowHeight[$i] = max($h, $this->iRowHeight[$i]);
			} else {
				$this->iRowHeight[$i] = $h;
			}
		}

		// Get maximum col width per columns
		for ($j = 0; $j < $n; ++$j) {
			$w = 0;
			for ($i = 0; $i < $m; ++$i) {
				$w = max($w, $this->iCells[$i][$j]->GetWidth($aImg));
			}
			if (isset($this->iColWidth[$j])) {
				$this->iColWidth[$j] = max($w, $this->iColWidth[$j]);
			} else {
				$this->iColWidth[$j] = $w;
			}
		}
	}

	public function _setcell($aRow, $aCol, $aVal = ''){
		if (isset($this->iCells[$aRow][$aCol])) {
			$this->iCells[$aRow][$aCol]->Set($aVal);
		} else {
			$this->iCells[$aRow][$aCol] = new GTextTableCell((string) $aVal, $aRow, $aCol);
			$this->iCells[$aRow][$aCol]->Init($this);
		}
	}

	public function StrokeWithScale($aImg, $aXScale, $aYScale){
		if (is_numeric($this->iScaleXPos) && is_numeric($this->iScaleYPos)) {
			$x = round($aXScale->Translate($this->iScaleXPos));
			$y = round($aYScale->Translate($this->iScaleYPos));
			$this->Stroke($aImg, $x, $y);
		} else {
			$this->Stroke($aImg);
		}
	}

	public function Stroke($aImg, $aX = null, $aY = null){
		if ($aX !== null && $aY !== null) {
			$this->iXPos = $aX;
			$this->iYPos = $aY;
		}

		$rc = $this->iSize[0]; // row count
		$cc = $this->iSize[1]; // column count

		if ($rc == 0 || $cc == 0) {
			Util\JpGraphError::RaiseL(27009);
		}

		// Adjust margins of each cell based on the weight of the grid. Each table grid line
		// is actually occupying the left side and top part of each cell.
		for ($j = 0; $j < $cc; ++$j) {
			$this->iCells[0][$j]->iMarginTop += $this->iBorderWeight;
		}
		for ($i = 0; $i < $rc; ++$i) {
			$this->iCells[$i][0]->iMarginLeft += $this->iBorderWeight;
		}
		for ($i = 0; $i < $rc; ++$i) {
			for ($j = 0; $j < $cc; ++$j) {
				$this->iCells[$i][$j]->AdjustMarginsForGrid();
			}
		}

		// adjust row and column size depending on cell content
		$this->_autoSizeTable($aImg);

		if ($this->iSize[1] != safe_count($this->iColWidth) || $this->iSize[0] != safe_count($this->iRowHeight)) {
			Util\JpGraphError::RaiseL(27008);
			//('Column and row size arrays must match the dimesnions of the table');
		}

		// Find out overall table size
		$width = 0;
		for ($i = 0; $i < $cc; ++$i) {
			$width += $this->iColWidth[$i];
		}
		$height = 0;
		for ($i = 0; $i < $rc; ++$i) {
			$height += $this->iRowHeight[$i];
		}

		// Adjust the X,Y position to alway be at the top left corner
		// The anchor position, i.e. how the client want to interpret the specified
		// x and y coordinate must be taken into account
		switch (strtolower($this->iXAnchor)) {
			case 'left':
				break;
			case 'center':
				$this->iXPos -= round($width / 2);

				break;
			case 'right':
				$this->iXPos -= $width;

				break;
		}
		switch (strtolower($this->iYAnchor)) {
			case 'top':
				break;
			case 'center':
			case 'middle':
				$this->iYPos -= round($height / 2);

				break;
			case 'bottom':
				$this->iYPos -= $height;

				break;
		}

		// Set the overall background color of the table if set
		if ($this->iBGColor !== '') {
			$aImg->SetColor($this->iBGColor);
			$aImg->FilledRectangle($this->iXPos, $this->iYPos, $this->iXPos + $width, $this->iYPos + $height);
		}

		// Stroke all cells
		$rpos = $this->iYPos;
		for ($i = 0; $i < $rc; ++$i) {
			$cpos = $this->iXPos;
			for ($j = 0; $j < $cc; ++$j) {
				// Calculate width and height of this cell if it is spanning
				// more than one column or row
				$cwidth = 0;
				for ($k = 0; $k < $this->iCells[$i][$j]->iColSpan; ++$k) {
					$cwidth += $this->iColWidth[$j + $k];
				}
				$cheight = 0;
				for ($k = 0; $k < $this->iCells[$i][$j]->iRowSpan; ++$k) {
					$cheight += $this->iRowHeight[$i + $k];
				}

				$this->iCells[$i][$j]->Stroke($aImg, $cpos, $rpos, $cwidth, $cheight);
				$cpos += $this->iColWidth[$j];
			}
			$rpos += $this->iRowHeight[$i];
		}

		// Stroke outer border
		$aImg->SetColor($this->iBorderColor);
		if ($this->iBorderWeight == 1) {
			$aImg->Rectangle($this->iXPos, $this->iYPos, $this->iXPos + $width, $this->iYPos + $height);
		} else {
			for ($i = 0; $i < $this->iBorderWeight; ++$i) {
				$aImg->Rectangle(
					$this->iXPos + $i,
					$this->iYPos + $i,
					$this->iXPos + $width - 1 + $this->iBorderWeight - $i,
					$this->iYPos + $height - 1 + $this->iBorderWeight - $i
				);
			}
		}
	}
}
